Skip to content

feat(channels): add xiaoyi channel support#875

Open
ystyle wants to merge 2 commits intosipeed:refactor/channel-systemfrom
ystyle:feat/xiaoyi-channel-refactor
Open

feat(channels): add xiaoyi channel support#875
ystyle wants to merge 2 commits intosipeed:refactor/channel-systemfrom
ystyle:feat/xiaoyi-channel-refactor

Conversation

@ystyle
Copy link

@ystyle ystyle commented Feb 27, 2026

Summary

  • Add XiaoYiChannel in pkg/channels/xiaoyi/ subpackage following new channel architecture
  • Implement A2A (Agent-to-Agent) protocol using xiaoyi-agent-sdk
  • Use BaseChannel with factory registration pattern
  • Add XiaoYiConfig to config.go
  • Add documentation with link to Huawei official setup guide

Files Changed

  • pkg/channels/xiaoyi/init.go - Factory registration
  • pkg/channels/xiaoyi/xiaoyi.go - Channel implementation
  • pkg/config/config.go - Add XiaoYiConfig
  • pkg/channels/manager.go - Register xiaoyi in initChannels
  • cmd/picoclaw/internal/gateway/helpers.go - Blank import
  • docs/channels/xiaoyi/ - Documentation (zh/en)
  • README.md / README.zh.md - Update channel list
  • go.mod / go.sum - Add SDK dependency

Configuration

{
  "channels": {
    "xiaoyi": {
      "enabled": true,
      "ak": "your-access-key",
      "sk": "your-secret-key",
      "agent_id": "your-agent-id"
    }
  }
}

Related

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are there changes to the Dockerfile regarding the channel modifications?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already resolved, it was a mistaken submission

@ystyle ystyle force-pushed the feat/xiaoyi-channel-refactor branch from b974a00 to ae50335 Compare February 27, 2026 14:42
@ystyle
Copy link
Author

ystyle commented Feb 27, 2026

Hi @alexhoshina, thank you for the review!

  1. Dockerfile changes: I've removed them in the latest commit (ae50335). The Dockerfile modifications were for local testing and should not be part of this PR.

  2. HandleMessage signature: I checked the `refactor/channel-system` branch, the `HandleMessage` function signature in `BaseChannel` is:

```go
func (c *BaseChannel) HandleMessage(
ctx context.Context,
peer bus.Peer,
messageID, senderID, chatID, content string,
media []string,
metadata map[string]string,
senderOpts ...bus.SenderInfo,
)
```

My implementation follows the same pattern as the `telegram` channel. I've also tested it locally - sending a message to XiaoYi and getting a response works correctly. Could you please double-check? Thanks!

README.md Outdated
## 💬 Chat Apps

Talk to your picoclaw through Telegram, Discord, WhatsApp, DingTalk, LINE, or WeCom
Talk to your picoclaw through Telegram, Discord, DingTalk, LINE, WeCom, or XiaoYi
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was WhatsApp deleted?

return nil
}

chatID := fmt.Sprintf("%s:%s", sessionID, taskID)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential impact on sessionID parsing needs to be analyzed, introducing new considerations: whether it will affect the parsing of sessionID

return fmt.Errorf("xiaoyi reply: %w", channels.ErrTemporary)
}

if err := c.client.SendStatus(ctx, taskID, sessionID, "Completed", "completed"); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the completed status published here conflict with long message fragmentation?

@alexhoshina
Copy link
Collaborator

Also, regarding the SDK you are using, perhaps it should also use a license compatible with Pico.

@alexhoshina
Copy link
Collaborator

Hi @alexhoshina, thank you for the review!

  1. Dockerfile changes: I've removed them in the latest commit (ae50335). The Dockerfile modifications were for local testing and should not be part of this PR.
  2. HandleMessage signature: I checked the refactor/channel-system branch, the HandleMessage function signature in BaseChannel is:

go func (c *BaseChannel) HandleMessage( ctx context.Context, peer bus.Peer, messageID, senderID, chatID, content string, media []string, metadata map[string]string, senderOpts ...bus.SenderInfo, )

My implementation follows the same pattern as the telegram channel. I've also tested it locally - sending a message to XiaoYi and getting a response works correctly. Could you please double-check? Thanks!

You are correct, I was referring to the old function signature, my apologies.

@ystyle
Copy link
Author

ystyle commented Feb 27, 2026

@alexhoshina Thanks for the feedback!

  1. WhatsApp deleted? - No, WhatsApp is not deleted. The latest commit (ae50335) only adds xiaoyi channel. Could you please check the latest version? The changed files are:

    • README.md, README.zh.md
    • cmd/picoclaw/internal/gateway/helpers.go (only adds xiaoyi import)
    • docs/channels/xiaoyi/ (new)
    • go.mod, go.sum
    • pkg/channels/manager.go
    • pkg/channels/xiaoyi/ (new)
    • pkg/config/config.go
  2. sessionID parsing with : - The chatID format is sessionID:taskID only for outbound messages. The sessionID itself doesn't contain :, so parsing should be safe.

  3. completed status vs long message fragmentation - Good point! The SendStatus("completed") is called after ReplyStream. If message fragmentation happens, the status might be sent too early. I should move the status call to ensure it's sent after all fragments. Let me check how other channels handle this.

@ystyle
Copy link
Author

ystyle commented Feb 27, 2026

@alexhoshina Good point about message fragmentation!

Currently, XiaoYi channel doesn't implement MessageLengthProvider (no MaxMessageLength set), so the Manager won't split messages. The SDK handles the message internally.

However, if we need to support MaxMessageLength in the future, the SendStatus("completed") call would need to be moved - perhaps to a callback or we could implement a separate interface for post-send actions.

For now, the implementation works correctly without fragmentation. Should I:

  1. Add a comment explaining this limitation?
  2. Or remove SendStatus("completed") and let the SDK handle the final state?

What do you suggest?

@alexhoshina
Copy link
Collaborator

@alexhoshina Thanks for the feedback!

  1. WhatsApp deleted? - No, WhatsApp is not deleted. The latest commit (ae50335) only adds xiaoyi channel. Could you please check the latest version? The changed files are:

    • README.md, README.zh.md
    • cmd/picoclaw/internal/gateway/helpers.go (only adds xiaoyi import)
    • docs/channels/xiaoyi/ (new)
    • go.mod, go.sum
    • pkg/channels/manager.go
    • pkg/channels/xiaoyi/ (new)
    • pkg/config/config.go
  2. sessionID parsing with : - The chatID format is sessionID:taskID only for outbound messages. The sessionID itself doesn't contain :, so parsing should be safe.

  3. completed status vs long message fragmentation - Good point! The SendStatus("completed") is called after ReplyStream. If message fragmentation happens, the status might be sent too early. I should move the status call to ensure it's sent after all fragments. Let me check how other channels handle this.

WhatsApp has been removed in RADEME, you can check the line numbers I've marked, but it's just a minor issue

@yinwm
Copy link
Collaborator

yinwm commented Feb 27, 2026

  • sessionID parsing with : - The chatID format is sessionID:taskID only for outbound messages. The sessionID itself doesn't contain :, so parsing should be safe.

Is xiaoyi need this chatID format?

- Add XiaoYiChannel in pkg/channels/xiaoyi/ subpackage
- Implement A2A protocol using xiaoyi-agent-sdk
- Use ReplyStream + SendStatus for long-task pattern
- Add XiaoYiConfig to config.go
- Register xiaoyi in manager.go initChannels
- Add blank import in gateway helpers
- Add documentation in docs/channels/xiaoyi/
- Update README.md and README.zh.md channel lists
@ystyle ystyle force-pushed the feat/xiaoyi-channel-refactor branch from ae50335 to 693db82 Compare February 27, 2026 16:12
@ystyle
Copy link
Author

ystyle commented Feb 27, 2026

@alexhoshina Sorry for the confusion! You were right about WhatsApp.

I found the issue - when I added XiaoYi to the README.md description line, I accidentally replaced "WhatsApp" instead of appending "XiaoYi".

This has been fixed in the latest commit (693db82):

  • Before: Talk to your picoclaw through Telegram, Discord, DingTalk, LINE, or WeCom (missing WhatsApp)
  • After: Talk to your picoclaw through Telegram, Discord, WhatsApp, DingTalk, LINE, WeCom, or XiaoYi

Thank you for catching this! 🙏

@ystyle
Copy link
Author

ystyle commented Feb 27, 2026

@yinwm Yes, the sessionID:taskID format is needed for XiaoYi channel because:

  1. Inbound: SDK's OnMessage callback provides both sessionID and taskID
  2. Outbound: When Agent replies, we need to call client.ReplyStream(taskID, sessionID, content) - so we need to extract both from chatID

The chatID passed through the message bus needs to carry both pieces of information, hence the sessionID:taskID format.

Alternative approaches:

  • Store taskID in metadata and only use sessionID as chatID
  • Use a custom struct instead of string

But the current : separator approach is simple and consistent with how other channels encode multiple IDs. Do you have a better suggestion?

Copy link

@nikolasdehor nikolasdehor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-structured channel implementation that follows existing patterns. The author has been responsive to feedback. Additional observations: (1) SDK license: As alexhoshina noted, the xiaoyi-agent-sdk dependency needs a compatible license (picoclaw uses Apache-2.0). The SDK repo should have an explicit license file before this PR merges. (2) chatID separator robustness: The sessionID:taskID format parsed with SplitN(msg.ChatID, ":", 2) works only if sessionID never contains ':'. Consider using a different separator or storing taskID in metadata. (3) Context usage: c.HandleMessage(c.ctx, ...) uses the channel-level context rather than the per-message ctx. If the channel is stopped mid-processing, this could cancel in-flight messages prematurely. (4) Error wrapping: In Send, the error is wrapped as channels.ErrTemporary regardless of the actual error type. Some errors (invalid session) may be permanent. Good work overall — pending license resolution.

@alexhoshina
Copy link
Collaborator

Well-structured channel implementation that follows existing patterns. The author has been responsive to feedback. Additional observations: (1) SDK license: As alexhoshina noted, the xiaoyi-agent-sdk dependency needs a compatible license (picoclaw uses Apache-2.0). The SDK repo should have an explicit license file before this PR merges. (2) chatID separator robustness: The sessionID:taskID format parsed with SplitN(msg.ChatID, ":", 2) works only if sessionID never contains ':'. Consider using a different separator or storing taskID in metadata. (3) Context usage: c.HandleMessage(c.ctx, ...) uses the channel-level context rather than the per-message ctx. If the channel is stopped mid-processing, this could cancel in-flight messages prematurely. (4) Error wrapping: In Send, the error is wrapped as channels.ErrTemporary regardless of the actual error type. Some errors (invalid session) may be permanent. Good work overall — pending license resolution.

Note that pico uses the MIT license

- Change chatID separator from ':' to '|' for robustness
- Use per-message ctx instead of channel-level c.ctx in HandleMessage
- Distinguish error types: ErrSessionNotFound -> ErrSendFailed (permanent)
- Update xiaoyi-agent-sdk dependency
@ystyle
Copy link
Author

ystyle commented Feb 28, 2026

@alexhoshina @nikolasdehor Thank you for the detailed review! I've addressed all the feedback in commit b3ff44f:

  1. SDK License ✅ - Added MIT license to xiaoyi-agent-sdk repository

  2. chatID separator robustness ✅ - Changed separator from : to | to avoid potential conflicts with sessionID content

  3. Context usage ✅ - Now using per-message ctx instead of channel-level c.ctx in HandleMessage

  4. Error wrapping ✅ - Now distinguishing error types:

    • ErrSessionNotFoundErrSendFailed (permanent)
    • Other errors → ErrTemporary (transient)

Please let me know if there are any other issues to address. Thanks!

@xiaket
Copy link
Collaborator

xiaket commented Mar 1, 2026

Given that the SDK you are using is a one man show, we would have concern over the supply chain security(I know go has checksums etc). I would be very cautious to move forward.

@ystyle
Copy link
Author

ystyle commented Mar 1, 2026

@xiaket Thank you for raising the supply chain security concern. That's a valid point.

The xiaoyi-agent-sdk is developed following Huawei's official TypeScript SDK: https://developer.huawei.com/consumer/cn/doc/service/openclaw-0000002518410344

The official installation method is:

openclaw plugins install @ynhcj/xiaoyi@latest

Our Go SDK implements the same A2A protocol as the official TS SDK.

I understand the concern about a single-maintainer dependency. A few options to address this:

  1. Transfer SDK to sipeed organization - Move the SDK under the sipeed org for collective maintenance
  2. Vendor the SDK code - Copy the SDK code directly into picoclaw codebase
  3. Add more maintainers - Invite picoclaw maintainers to collaborate on the SDK

What would you suggest as the best approach to move forward?

@xiaket
Copy link
Collaborator

xiaket commented Mar 1, 2026

Thanks @ystyle ! I think @Zepan might be the best person to answer this question ;)

@xiaket
Copy link
Collaborator

xiaket commented Mar 1, 2026

From my personal perspective, I don't think it's fair to ask sipeed/picoclaw to support an SDK that has very little usage.

@ystyle
Copy link
Author

ystyle commented Mar 2, 2026

@xiaket You're right about the current usage. However, Huawei's OpenClaw agent type was just released about two weeks ago. The ecosystem is still very new.

As the first (and currently only) Go SDK for OpenClaw, usage will naturally grow as more developers adopt the platform. This PR would enable picoclaw users to connect to Huawei's XiaoYi ecosystem early.

But I completely understand the concern. Let's wait for @Zepan's input on how to proceed.

Options remain:

  1. Transfer SDK to sipeed org
  2. Vendor the SDK code into picoclaw
  3. Wait for more adoption before merging

Thank you for the thoughtful review! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants